# 字符串
# String(java.lang)
# 简介
String效果上相当于char[](Java 8 及之前),但底层是被final修饰的byte[](Java 9 及之后)。且其实现了Serializable,Comparable,CharSequence接口。String类及其所有属性都被声明为final,是常量,其值(地址值,不是底层数组)在创建后不能被更改。
# 不可变性
由于它的底层结构,具有不可变性:
当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的 value 进行赋值
当执行
concat,substring,replace等操作都需要重新指定内存区域赋值(与常量池无关)字符串的
+运算,若是存在变量相加,先开空间再拼接,结果在堆中;只有常量则先拼接(类似字面量赋值),然后在常量池中找,有就返回,没有就创建并放入常量池。如果拼接的结果调用intern()方法,返回值就在常量池中。字符串常量池中不会存储相同内容的字符串。加
final修饰的也是常量!String str1 = "hello"; String str2 = "hello"; char[] charArray = {'h', 'e', 'l', 'l', 'o'}; String str3 = new String(charArray); String str4 = "world"; String str5 = "helloworld"; System.out.println(str1 == str2);//true System.out.println(str1 == str3);//false,同理str2!=str3 System.out.println(str5 == str1 + str4);//false System.out.println(str5 == "hello" + "world");//true System.out.println(str5 == str1 + "world");//false System.out.println(str5 == (str1 + "world").intern());//true System.out.println(str3.equals(str1));//true // 注意final String s1 = "helloworld"; String s2 = "hello"; String s3 = s2 + "world"; System.out.println(s1 == s3);// false String s11 = "helloworld"; final String s22 = "hello"; String s33 = s22 + "world"; System.out.println(s11== s33);// true通过字面量直接赋值的定义字符串的会放入(Java8)方法区(具体实现为元空间)的字符串常量池中;但是**
new String方式定义的不会放入**。

特别的,注意对象中的 String 属性
Person t1 = new Person("tom", 1); t1.setName("hh"); t1.name = "xx"; Person t2 = new Person("tom", 1); t2.setName("hh"); t2.name = "xx"; System.out.println(t1.name == t2.name);// "=="比较地址值,三处赋值操作都为 true,因此值在常量池中!
# 构造方法
String():初始化一个新创建的 String 对象,使其表示一个空字符序列String(String original):初始化一个新创建的 String 对象String(char/byte[] value [,int offset,int count]):分配一个新的 String,它包含取自字符数组参数全部(或一个子数组)的字符String(byte[] bytes[,int offset,int length][,String charsetName]):通过使用指定的字符集解码指定的 byte (或子)数组,构造一个新的 StringString s = "hello":直接创建新的 String字面值作为字符串对象(新创建的字符串是该参数字符串的副本)和通过构造方法创建对象的不同
String s = new String("hello"); String s = "hello";前者最多创建两对象,后者最多创建一个对象
……
# 常用方法
TIP
不改变原 String 值
判断功能
boolean isEmpty()字符串长度是否为 0boolean contains(CharSequence cs)字符串中是否包含指定字符序列boolean equals(Object anObject)字符串与指定字符串是否相等(内容),推荐常量放前面boolean equalsIgnoreCase(String str)字符串与指定字符串是否相等,忽略大小写boolean startsWith(String prefix[,int toffset])从指定索引开始的子串是否以 prefix 开始boolean endsWith(String suffix)字符串是否以 suffix 后缀结尾boolean matches(String regex)判断此字符串是否匹配给定的正则表达式
获取功能
int length():返回字符串长度String concat(String str)将字符串拼接,本字符串不变,常用+char charAt(int index)返回指定索引的字符int indexOf/lastIndexOf(int ch/String str)获取指定字符/字符串第一次/最后一次出现的索引int indexOf/lastIndexOf(int ch/String str,int fromIndex)获取指定~从**[某索引开始第一次出现的索引/最后一次出现的索引(从指定的索引开始反向搜索**,但返回值还是正向数数)ch是int类型原因:'a'和97都能代表'a'String s = "qqqqqaq"; int a = s.lastIndexOf("a",5);//大于等于5都能搜到,值为5;小于则返回值为-1String substring(int startIndex[,int endIndex])获取[start,end)子串,没 end 到结尾
替换
String replace(CharSequence/char target, CharSequence/char replacement)将所有target 字符或字符串替换为新的 replacement 字符串。CharSequence 是接口,String 实现了它。用于敏感词汇过滤String replaceAll(String regex, String replacement)使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串String replaceFirst(String regex, String replacement)使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串
转换功能
String static valueOf(int i/char[] chs)静态方法将 int 型和字符数组型数据转为字符串String toLowerCase() /toUpperCase()将所有字符都转换为小写/大写,本身不变byte[] getBytes([Charset c/String c])将字符串转换为字节数组;相反则直接构造器传入char[] toCharArray()将字符串转换为字符数组;相反则直接构造器传入
其他功能
去空格(去掉字符串首尾空格)
String trim()按字典比较(按字典顺序比较,在 str 前返回负数,在 str 后返回正数,相等则返回 0)
int compareTo(String str)/compareIgnoreCase(String str)切片:根据给定正则表达式 regex的匹配切片,返回字符串数组,不包括 regex 字符串。最多不超过 limit 个,如果超过了,剩下的全部都放到最后一个元素中。若要用英文句点
.切分,必须写\\.。String[] split(String regex [,int limit])String fbIds = "4542,4540,"; String[] split = fbIds.split(","); System.out.println(split.length);// 2若常量池中包括了一个等于此 String 对象的字符串(由 equals 确定),则返回池中字符串;否则将此字符串添加到池中,并返回其引用
inter()
# 类型转换
String 与 基本类型或包装类型之间转换
- 基本类型 ——> 字符串:
基本类型的值+"":最简单方法,常用- String 类的静态方法
valueOf(参数) - 包装类的静态方法
toString(参数),不是 Object 类的toString()方法,重载
- 字符串 ——> 基本类型:
- 包装类的静态方法
parseXxx() - 包装类的静态方法
valueOf(参数),转包装类后再转基本类型
- 包装类的静态方法
# 习题
字符串遍历
char charAt(int index)或char[] toCharArray()统计大串中小串出现的次数
int indexOf(String str,int fromIndex)或substring把字符串的首字母转成大写,其他小写
String substring(int start,int end)把一个小数转换为保留两位小数的字符串,不考虑四舍五入:加空串转为字符串,再用 split 即可
- 若考虑四舍五入,则可以将保留的数转为整数,再根据后一位值对整数进行加 1 与否
字符串必须只有字母和数字,如下是不是这种情况的:
(ch<='0'||ch>='9') && (ch<='A'||ch>='Z') && (ch<='a'||ch>='z')
# StringBuilder(java.lang)
StringBuffer 比 StringBuilder 每个方法都多 synchronized
StringBuilder(字符串缓冲区),又称为可变字符序列,表示字符容器,其内容和长度可变,不同步,线程不安全。没有overrideObject 类的equals()方法,不能像 String 类对象可以用操作符+进行连接。
# 底层分析
它的底层是一个**
char[]**(Java 9 后为byte[]),用来存放字符串内容调用构造方法(默认长度为
s.length()+16)或添加字符串时都会调用append(),并更新count即 StringBuilder 的长度,可通过length()获取它还有个属性
capacity代表 StringBuilder 的容量,即底层数组的length,可通过capacity()获取若添加的数据导致底层数据存不下了(大于),需扩容底层数组:
先通过
count+s.length()减去底层数组的length即 StringBuilder 的capacity(),若大于 0 则扩容调用
Arrays.copyOf(char[] original,int newLength)将原数组拷贝到新数组,底层为System.arraycopy,其中newLength为底层数组原有capacity左移一位(即乘 2)并加 2若左移一次后还不够,则直接用
count+s.length();甚至左移后超出范围变负值(之后再总结)
# 构造方法:
StringBuilder();构造一个空的 StringBuilder 容器。StringBuilder(String str):构造一个 StringBuilder 容器,并将字符串添加进去。StringBuilder(int capacity):构造一个 StringBuilder 容器,并设置初始capacity。推荐使用。
# 常用方法
在原 StringBuilder 对象中改变并返回本身,原对象也改变
增
StringBuilder append(Object o)添加任意类型数据的字符串形式到末尾,并返回当前对象自身StringBuilder append(CharSequence c,int start,int end):插入其中[start,end)部分StringBuilder insert(int offset,Object o)在字符串的offset 位置插入任意对象,其余后移
删
StringBuilder delete(int start,int end)删除从**[start 到 end)**的字符串StringBuilder deleteCharAt(int index)删除指定位置字符
改
StringBuilder replace(int start,int end,String s)从**[start 到 end)**的对象替换为指定字符串void setCharAt(int index,char ch)替换指定位置的字符
查
char charAt(int n)返回指定索引处的字符int indexOf(String str)返回指定字符串所在索引
反转
StringBuilder reverse()反转
截取(返回值类型不再是 StringBuilder 本身,原有对象不改变)
String substring(int start,int end)截取子串,没有 end 参数时截取到尾部
其他
int length()返回 StringBuilder 的长度(不是 capacity)void setLength(int newLength)设置 StringBuilder 长度int capacity()返回 StringBuilder 的容量public String toString()将当前 StringBuilder 对象转换为 String 对象
String 和 StringBuilder 转换
String ---> StringBuilder 通过构造函数传入或构造好之后 append()
StringBuilder ---> String 调用
toString()或 String 类的valueOf()静态方法String str1 = sb.toString(); String str2 = String.valueOf(sb);
# 试题
String、StringBuffer、StringBuilder 区别
- String 内容、长度不可变,后两个可变。String 拼接时,总是会在内存中创建一个新对象,影响性能
- StringBuffer 是同步的,数据安全,效率低;StringBuilder(5.0 新增)是不同步的,数据不安全,效率高
- 底层都是
char[](java8 中);后两个初始创建长度为s.length()+16的数组,
StringBuilder 和数组的区别
二者都可以看做装其他数据的容器,但是 StringBuilder 的数据最终是一个字符串数据,而数组可以存放多种类型的数据,但必须是同一种数据类型的
参数传递问题
注意:(以下画图即可理解,如下图)
包装类、String 作为引用数据类型,是常量,具有不可变性。
StringBuffer、StringBuilder 在赋值时不改变内容,调用方法时改变
【重点】在于 String 内容不可变并且变量相加需要开空间再拼接,StringBuilder 等内容可以变
String s1 = "hello";
String s2 = "world";
System.out.println(s1+"---"+s2);//hello---world
change(s1,s2);
System.out.println(s1+"---"+s2);//hello---world
StringBuffer sb1 = new StringBuffer("hello");
StringBuffer sb2 = new StringBuffer("world");
System.out.println(sb1+"---"+sb2);//hello---world
change(sb1,sb2);
System.out.println(sb1+"---"+sb2);//hello---worldworld
public static void change(String s1, String s2) {
s1 = s2;
s2 = s1+s2;
}
public static void change(StringBuffer sb1, StringBuffer sb2) {
sb1 = sb2;
sb2 = sb1.append(sb2);
}

# StringJoiner(java.util)
Java8 新增工具类,低层使用
StringBuilder实现
StringJoiner 用于构造由 delimiter(界定符) 分隔的字符序列,并可选地以提供的prefix开头和以提供的suffix结尾。
在将某些内容添加到StringJoiner之前,其sj.toString()方法默认情况下将返回前缀 + 后缀。但是,如果调用setEmptyValue()方法,则将返回提供的 emptyValue 。例如,在使用集合符号创建表示空集合的字符串(即{},前缀为{,后缀为}且未向 StringJoiner 添加任何内容)时,可以使用此方法。
例子:字符串
[George:Sally:Fred]可能是由如下如下方式构造的:StringJoiner sj = new StringJoiner(":", "[", "]"); sj.add("George").add("Sally").add("Fred"); String desiredString = sj.toString();
# Stream(java.util)
底层使用
StringJoiner实现
语法
java.util.stream.Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)例子(效果同
StringJoiner一样)List<Integer> numbers = Arrays.asList(1, 2, 3, 4); String commaSeparatedNumbers = numbers.stream() .map(i -> i.toString()) .collect(Collectors.joining(", "));